package com.zerocarbon.framework.web.aspect;

import com.zerocarbon.framework.common.constants.ContextConstants;
import com.zerocarbon.framework.common.dto.BaseDTO;
import com.zerocarbon.framework.common.enums.BaseExceptionCode;
import com.zerocarbon.framework.common.result.Result;
import com.zerocarbon.framework.web.extension.ValidRestAuth;
import com.zerocarbon.framework.web.extension.ValidRestSign;
import com.zerocarbon.framework.web.extension.ValidateResult;
import com.zerocarbon.framework.web.handler.IValidRestHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.core.annotation.Order;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * 验证用户信息、签名验证 切面
 * @author wyf  2020年6月10日
 */
@Slf4j
@Aspect
@Order(-20)
@ConditionalOnClass({HttpServletRequest.class, RequestContextHolder.class}) 
public class ValidRestAspect {
	
	private IValidRestHandler iValidRestHandler;
	
	public ValidRestAspect(IValidRestHandler iValidRestHandler) {
		this.iValidRestHandler = iValidRestHandler;
	}

	@Around("execution(public * com.zerocarbon..*.*Controller.*(..)) || execution(public * com.zerocarbon..*.*RestController.*(..))" )
	public Object handlerMethod(ProceedingJoinPoint point) throws Throwable {
		if (!(point.getSignature() instanceof MethodSignature)) {
        	log.debug("no HandlerMethod invoke!");
        	return point.proceed();
        }
        //从方法处理器中获取出要调用的方法
		Method method = ((MethodSignature) point.getSignature()).getMethod();
		Class<?> classT = point.getTarget().getClass();
        //token验证
        if(method.isAnnotationPresent(ValidRestAuth.class) || classT.isAnnotationPresent(ValidRestAuth.class)) {
        	ValidateResult<?> result = this.validRestToken();
			if(result!=null && !result.isSuccess()) {
				if(result.getResult()!=null) {
					return result.getResult();
				} else
					return Result.fail(BaseExceptionCode.TOKEN_INVALID_EX.getCode(), BaseExceptionCode.TOKEN_INVALID_EX.getMessage());
				
			}
		}
        
        //验证签名
        if(method.isAnnotationPresent(ValidRestSign.class) || classT.isAnnotationPresent(ValidRestSign.class)) {
        	ValidateResult<?> result = this.validRestSign(point.getArgs());
			if(result!=null && !result.isSuccess()) {
				if(result.getResult()!=null) {
					return result.getResult();
				} else
					return Result.fail(BaseExceptionCode.SGIN_EX.getCode(), BaseExceptionCode.SGIN_EX.getMessage());
				
			}
		}
    	return point.proceed();
	}
	
	 /**
     * token验证
     * @return
     * @throws Exception
     */
    private <T> ValidateResult<T> validRestToken() throws Exception {
    	HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
		String token = request.getHeader(ContextConstants.BEARER_HEADER_KEY);
		 if (StringUtils.isBlank(token))  //header为空，查参数
	            token = request.getParameter(ContextConstants.JWT_KEY_TOKEN);
		return iValidRestHandler.validateAuth(token);
	}
    
    /**
	 * 验证签名，这里只验证第一个参数，并且参数是BaseDTO
	 * @param args
	 * @return
	 * @throws Exception
	 */
	private <T> ValidateResult<T> validRestSign(Object[] args) throws Exception {
		if(args==null || args.length==0) {
			log.error("[invoke] invocation.getArguments() is null");
			return new ValidateResult<>(false, Result.fail(BaseExceptionCode.ILLEGALA_ARGUMENT_EX.getCode(), BaseExceptionCode.ILLEGALA_ARGUMENT_EX.getMessage()));
		}
		Object param = args[0]; //第一个参数
		if(param instanceof BaseDTO) {
			return iValidRestHandler.validateSgin((BaseDTO)param, iValidRestHandler.getAppSecret(((BaseDTO) param).getAppKey()));
		} else {
			log.error("[invoke] param is not BaseDTO");
			return new ValidateResult<>(false, Result.fail(BaseExceptionCode.ILLEGALA_ARGUMENT_EX.getCode(), BaseExceptionCode.ILLEGALA_ARGUMENT_EX.getMessage()));
		}
	}
}
